package com.zhs.study.aop;//package com.zhs.study.aop;
//
//
//import com.zhs.study.common.R;
//import com.zhs.study.dto.UserDto;
//import com.zhs.study.exception.ServiceException;
//import com.zhs.study.util.RedisUtil;
//import lombok.extern.slf4j.Slf4j;
//import org.apache.commons.lang3.StringUtils;
//import org.aspectj.lang.JoinPoint;
//import org.aspectj.lang.annotation.*;
//import org.springframework.stereotype.Component;
//import org.springframework.web.context.request.RequestContextHolder;
//import org.springframework.web.context.request.ServletRequestAttributes;
//
//import javax.annotation.Resource;
//import javax.servlet.http.HttpServletRequest;
//import java.text.SimpleDateFormat;
//import java.util.Date;
//import java.util.Map;
//import java.util.concurrent.TimeUnit;
//
///**
// * 接口调用情况监控
// * 1、监控单个接口一天内的调用次数
// * 2、如果抛出异常，则记录异常信息及发生时间
// * 3、对单个IP进行限流，每天对每个接口的调用次数有限
// *
// * aop主要是通过第一个bean的后置处理器，找到@AspectJ这个注解的所有类
// * 并在这个类的里面找到所有的befeore，after等注解的方法，每一个before，after等都会生成一个对应的advisor
// * 每个advisor包括advise和pointcut，advise主要是用来作为一个增强器的使用，pointcut是为了进行匹配，
// * 匹配成功才进行最终的动态代理的生成最后获取到所有的advisors，由于可能有大量的advisor，
// * 因此在bean的最后一个后置处理器才对这些所有的advisor进行处理，即在bean进行初始化之后才进行处理。
// * 最后会去循环遍历这些advisors，通过advisors里面封装的pointcut和生成的advisor进行比较，
// * 如果匹配成功，则说明bean需要创建动态代理
// */
//@Aspect
//@Component
//@Slf4j
//public class ApiCallAdvice {
//
//    @Resource
//    private RedisUtil redisUtil;
//
//    private static final String FORMAT_PATTERN_DAY = "yyyy-MM-dd";
//    private static final String FORMAT_PATTERN_MONTH = "yyyy-MM";
//    private static final String FORMAT_PATTERN_MILLS = "yyyy-MM-dd HH:mm:ss:SSS";
//
//    /**
//     * 真正执行业务操作前先进行限流的验证
//     * 限制维度为：一天内单个IP的访问次数
//     * key = URI + IP + date（精确到天）
//     * value = 调用次数
//     * execution : 在方法执行时触发
//     * * ： 返回任意类型
//     * com.zhs.study.controller.*：全路径包名 + 类名
//     * * ： 方法名
//     * (..) : 参数
//     */
//    @Before("execution(* com.zhs.study.controller.*.*(..))")
//    public void before() {
//        log.info("===========before方法被调");
//        String uri = getRequestURI();
//        String date = dateFormat(FORMAT_PATTERN_DAY);
//        String ip = getRequestIp();
//        if (StringUtils.isEmpty(ip)) {
//            throw new ServiceException("IP不能为空。");
//        }
//        // URI+IP+日期 构成以天为维度的key
//        String ipKey = uri + "_" + ip + "_" + date;
//        if (redisUtil.get(ipKey) != null) {
//            long size = Long.parseLong(redisUtil.get(ipKey) + "");
//            if (size > 10000) {
//                throw new ServiceException("访问失败，已超过访问次数。");
//            }
//            redisUtil.set(ipKey, size + 1);
//        } else {
//            redisUtil.set(ipKey, 1L, 1, TimeUnit.DAYS);
//        }
//    }
//
//    /**
//     * 如果有返回结果，代表一次调用，则对应接口的调用次数加一，统计维度为天
//     * （Redis使用Hash结构）
//     * key = URI
//     * key = date （精确到天）
//     * value = 调用次数
//     */
//    @AfterReturning("execution(* com.zhs.study.controller.*.*(..))")
//    public void afterReturningDay() {
//        log.info("===========AfterReturning方法被调 =====显示年月日");
//        String date = dateFormat(FORMAT_PATTERN_DAY);
//
//        if (redisUtil.get(date) != null) {
//            long size = Long.parseLong(redisUtil.get(date)+"");
//            redisUtil.set(date, size + 1);
//        } else {
//            redisUtil.set(date, 1L, 365, TimeUnit.DAYS);
//        }
//    }
//
//    @Pointcut("execution(* com.zhs.study.controller.UserController.getById(..))")
//    public void getById(){}
//
//    /**
//     * 如果有返回结果，代表一次调用，则对应接口的调用次数加一，统计维度为天
//     * （Redis使用Hash结构）
//     * key = URI
//     * key = date （精确到天）
//     * value = 调用次数
//     */
//    @AfterReturning(returning = "data", pointcut = "getById()")
//    public void loginLog(JoinPoint joinPoint, Object data) {
//        R<Map> r = (R<Map>) data;
//        Map rDat = (Map) r.getData();
//        UserDto user = (UserDto) rDat.get("user");
//        String userId = user.getId() + "";
//        String date = dateFormat(FORMAT_PATTERN_DAY);
//        if (redisUtil.get(userId) == null) {
//            redisUtil.set(userId, date, 30, TimeUnit.DAYS);
//        }
//    }
//
//    /**
//     * 如果有返回结果，代表一次调用，则对应接口的调用次数加一，统计维度为月
//     * （Redis使用Hash结构）
//     * key = URI
//     * key = date （精确到天）
//     * value = 调用次数
//     */
//    @AfterReturning("execution(* com.zhs.study.controller.*.*(..))")
//    public void afterReturningMonth() {
//        log.info("===========AfterReturning方法被调 ===== 只显示年月");
//        String date = dateFormat(FORMAT_PATTERN_MONTH);
//
//        if (redisUtil.get(date) != null) {
//            long size = Long.parseLong(redisUtil.get(date)+"");
//            redisUtil.set(date, size + 1);
//        } else {
//            redisUtil.set(date, 1L, 365, TimeUnit.DAYS);
//        }
//    }
//
//    /**
//     * 如果调用抛出异常，则缓存异常信息（Redis使用Hash结构）
//     * key = URI + “_exception”
//     * key = time (精确到毫秒的时间)
//     * value = exception 异常信息
//     *
//     * @param ex 异常信息
//     */
//    @AfterThrowing(value = "execution(* com.zhs.study.controller.*.*(..))", throwing = "ex")
//    public void afterThrowing(Exception ex) {
//        log.info("===========AfterThrowing方法被调");
//        String time = dateFormat(FORMAT_PATTERN_MILLS);
//        String exception = ex.getMessage();
//        redisUtil.set(time, exception, 365, TimeUnit.DAYS);
//    }
//
//    private String getRequestURI(){
//        // 接收到请求，记录请求内容
//        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//        //获取请求的request
//        if (attributes != null) {
//            return attributes.getRequest().getRequestURI();
//        } else {
//            return null;
//        }
//    }
//
//    private String getRequestIp() {
//        // 接收到请求，记录请求内容
//        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//        //获取请求的request
//        if (attributes == null) {
//            return null;
//        }
//        HttpServletRequest request = attributes.getRequest();
//        // 获取请求IP
//        String ip = request.getHeader("x-forwarded-for");
//        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip) || "null".equals(ip)) {
//            ip = "" + request.getHeader("Proxy-Client-IP");
//        }
//        if (ip.length() == 0 || "unknown".equalsIgnoreCase(ip) || "null".equals(ip)) {
//            ip = "" + request.getHeader("WL-Proxy-Client-IP");
//        }
//        if (ip.length() == 0 || "unknown".equalsIgnoreCase(ip) || "null".equals(ip)) {
//            ip = "" + request.getRemoteAddr();
//        }
//        return ip;
//    }
//
//    private String dateFormat(String pattern) {
//        SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
//        return dateFormat.format(new Date());
//    }
//}
